home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
vol_200
/
297_01
/
prlush.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-12-30
|
28KB
|
1,031 lines
/* prlush.c */
/* Lush resolution .
* Along with unification these are the most important routines in Prolog.
* If you want to embed Small Prolog then you won't need query_loop as such.
* See Chris Hogger's "An Introduction to Logic Programming" (Academic Press)
* if you are hungry for more explanation.
*/
/* Small Prolog uses a control stack , a substitution stack and a
* "trail" to represent its run-time state.
* The control stack is the stack of "activation records".
* A clause packet is the linked list of clauses that correspond to
* a predicate. It's prolog's equivalent of a procedure.
* You enter a procedure when a goal is unified successfully with the
* head of a clause. You can enter the procedure at different clauses.
* Execution tries the clauses in the order of their occurence in the
* list.
* Each time a procedure (clause packet) is entered, a corresponding "frame"
* (or what is called "activation record" in languages like C)
* is pushed on the control stack, and a corresponding frame is pushed
* on the substitution stack(representing the parameters).
* However the situation is much more complicated than what occurs in
* familiar languages because a procedure might have to be redone
* (with the next clause of the packet) after exit of the procedure.
* So you cant just pop the frame on successful return.
* Popping the frame only occurs on backtracking, unless you implement
* an optimisation that recognises that no more clauses could be tried.
* I have not implemented this optimisation in the current version, sorry.
* Each frame must remember its parent: a pointer to the frame of
* the clause which contains the goal that led to the current clause.
* This is used when every goal of the clause has been successfully solved.
* This is not necessarily the previous frame because the previous frame
* comes from the elder brother goal.
* We need to remember also the next goal to try if the current goal
* ends up being successful.
* We need to remember the last "backtrack point": this is a frame
* where there seemed to be the possibility of another solution
* for its procedure.
* We backtrack to that point when there is a failure.
* There is a global variable that does this. But this variable
* will have to be updated when we backtrack so we need to save
* the previous values of it on the stack too. To save a little
* space only some frames need to store the last backtrack point,
* These are "non-deterministic frames", frames in which there
* was a remaining candidate to the current clause.
* Sometimes substitions are created low in the substitution stack
* -before the environment of the current goal. This is why
* we use a trail to be able to unset these substitutions on backtracking.
* We have been lazy and recorded all substitutions on the
* trail.
*/
#include <stdio.h>
#include <setjmp.h> /* added this on Dec 21 1991 */
#include <assert.h> /* added this on Dec 21 1991 */
#include "prtypes.h"
#include "prlush.h"
#define NOTVARPRED "A variable can't be used as a predicate\n"
#define NOPRED "Predicate not atom\n"
#define STACKCONTENTS "Ancestors of current goal:\n"
#define INIQUERY "Syntax error ini initial query"
#define STRINGQUERY "Syntax error ini query passed as string"
#if TRACE_CAPABILITY
/* values of where in tracing */
#define G_BTK 'B' /* goto bactrack */
#define G_AGA 'P' /* goto AGAIN */
#define KEEP_GOING 'K'
#define TRACE(X) if(Trace_flag > 0){\
if(X == 0)return ABNORMAL_LUSH_RETURN;\
switch(where){case G_AGA:goto AGAIN;case G_BTK:goto BACKTRACK;}}
#define CAN_SKIP 1
#define CANT_SKIP 0
#else
#define TRACE(X)
#endif
/* Although the Trace_flag can be set by a builtin
actual tracing can be suspended while stepping over a call .
The following two variables are used for this purpose.
*/
static int Copy_tracing_now;
#ifdef HUNTBUGS
extern int Bug_hunt_flag;
#endif
extern subst_ptr_t DerefSubst; /* enviroment of last dereferenced
object */
extern subst_ptr_t my_Subst_alloc();
extern node_ptr_t DerefNode; /* skeleton of last dereferenced object */
extern node_ptr_t NilNodeptr; /* node corresponding to empty list */
extern atom_ptr_t Nil; /* object of empty list */
extern clause_ptr_t Bltn_pseudo_clause;
extern dyn_ptr_t HighDyn_ptr,Dyn_mem, Dyn_ptr, my_Dyn_alloc(); /* for control stack */
extern subst_ptr_t Subst_mem, Subst_ptr; /* for substitution stack */
extern node_ptr_t **Trail_mem, **Trail_ptr;
extern FILE * Curr_outfile;
void ini_lush(), reset_zones();
clause_ptr_t Curr_clause; /* current clause containing Goals */
clause_ptr_t Candidate; /*Used when we look for a clause whose
head might match goal */
dyn_ptr_t QueryCframe; /* Control frame of Query */
dyn_ptr_t LastCframe; /* most recent Cframe */
dyn_ptr_t LastBack; /* points to cframe of most recent backtrack point */
dyn_ptr_t Parent; /* parent Cframe of current goal */
dyn_ptr_t BackFrame; /* parent frame in case of reverse tracing */
node_ptr_t Goals; /* what remains of current goals of the clause.
The head of the list is the goal to satisfy first.
Note that there will be (in general) other goals to
satisfy on completion of solution of Goals
*/
node_ptr_t Query; /* the initial query */
node_ptr_t Arguments;/* arguments of current goal (points to a list) */
subst_ptr_t Subst_goals, /* substituion env of Goals */
SubstGoal, /* can be different to Subst_goals if there is a "call" */
OldSubstTop; /* for backtracking */
atom_ptr_t Predicate; /* predicate of current goal */
node_ptr_t **OldTrailTop;
int ErrorGlobal; /* used to communicate the fact there was an error */
int Deterministic_flag; /*used to minimise "more?" questions after a solution */
int ReverseTraceMode = 0; /* if set to 1 means you can trace backwards
but this is greedy on control stack space
because all frames are like the non-deterministic
frames.
*/
integer Nunifications = 0; /* just for statistics */
/******************************/
/* Dec 21 1991 new variables: */
/******************************/
node_ptr_t ND_builtin_next_nodeptr;/* This is for
non deterministic builtins. When this variable is NULL it
is the first call of the predicate. Otherwise it is the nodeptr
that guides the non deterministic builtin.
*/
jmp_buf Bltin_env; /* see do_builtin() */
jmp_buf Query_jenv; /* see query_loop() */
int QJusable = 0; /* Determines if we can longjump to Query_jenv */
#if TRACE_CAPABILITY
extern int Trace_flag; /* sets trace mode */
extern int Tracing_now; /* sets trace mode */
dyn_ptr_t Skip_above;
int Unleash_flag = 0;
#endif
/*******************************************************************
read_goals()
Called by query_loop.
Updates Goals, Nvars and Query.
*******************************************************************/
read_goals(ifp)
FILE *ifp;
{
extern node_ptr_t read_list(), get_node();
extern FILE * Curr_infile;
node_ptr_t head;
FILE * save_cif;
ENTER("read_goals");
save_cif = Curr_infile;
Curr_infile = ifp;
Goals = read_list(PERMANENT);
if(Goals == NULL)return(0);
copy_varnames();
head = NODEPTR_HEAD(Goals);
if(NODEPTR_TYPE(head) == ATOM)
{
pair_ptr_t pairptr, get_pair();
pairptr = get_pair(DYNAMIC);
NODEPTR_TYPE(PAIRPTR_HEAD(pairptr)) = PAIR;
NODEPTR_PAIR(PAIRPTR_HEAD(pairptr)) = NODEPTR_PAIR(Goals);
NODEPTR_TYPE(PAIRPTR_TAIL(pairptr)) = ATOM;
NODEPTR_ATOM(PAIRPTR_TAIL(pairptr)) = Nil;
Goals = get_node(DYNAMIC);
NODEPTR_TYPE(Goals) = PAIR;
NODEPTR_PAIR(Goals) = pairptr;
}
Query = Goals;
Curr_infile = save_cif;
return(1);
}
/******************************************************************************
initial_query
This executes the query from a file . It is silent if the file does not exist.
******************************************************************************/
initial_query(filename)
char *filename;
{
FILE *ifp;
ENTER("initial_query");
if((ifp = fopen(filen